/*********************************************************************
                             shared.c
********************************************************************/
#include <sys/file.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>
#include <semaphore.h>
#include "clam.h"

void Zserver_fill();
int Zcreate_node_shared(int i, int procID);

extern int cnt_overlap, cnt_nodes;

sem_t pool_mutex;	/* mutex for getting a node from the pool */
sem_t cmp_mutex;	/* mutex for calculating number of comparisons */

int Num_threads=0;

int *local_cnt_cmp;
int *local_cnt_olap;
int *local_cnt_nodes;

int *startRow, *endRow;
int row_fetched;

void* worker(void *arg) {
  int i;
  int start,end;
  int myid = (int) arg;
  
  start = startRow[myid];
  end   = endRow[myid];   
    
  for(i=start; i<=end ; i++) {      
     if(!Zcreate_node_shared(i, myid)) 
	printf("Error in Zcreate node\n");      
  }    
  return 0;
}

int shared_node_creation(void) {
  
  pthread_t *threads;
  pthread_attr_t attr;
   
  int i, usedProcs, curProc;
  double compPerProc, computations, actualComp[20];
  long long int  matrixRow; 
  
  pthread_attr_init(&attr);
  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
  usedProcs = 0; 
    
  if (Num_threads < 1) { /* why would this happen */
  	printf("Shared memory implementation: \n");
	Num_threads = 1; // WMN change for MAC  
  }
  	
  threads = (pthread_t *)malloc(sizeof(pthread_t)*Num_threads);
  if(!threads){
    printf("Allocation for threads failed. Aborting\n");
    exit(1);
  }
  
  sem_init(&cmp_mutex,0,1);  
  
  local_cnt_cmp   = (int*)malloc(sizeof(int)* Num_threads);
  local_cnt_olap  = (int*)malloc(sizeof(int)* Num_threads);
  local_cnt_nodes = (int*)malloc(sizeof(int)* Num_threads);
  if (!local_cnt_cmp  || !local_cnt_olap || !local_cnt_nodes) {
      printf("Allocation for local counts failed\n");
      return 0;
  }
  
  for (i=0; i<Num_threads; i++) {
  	local_cnt_cmp[i] = 0;
	local_cnt_olap[i] = 0;
	local_cnt_nodes[i] = 0;
  }
  
   /* chunk of rows to be processed by each thread */
   startRow = (int*)malloc(sizeof(int)* Num_threads);
   endRow   = (int*)malloc(sizeof(int)* Num_threads);
    
   if (!startRow || !endRow) {
       printf("Allocation for preallocate rows failed.\n");
       return 0;
   }
    
   /* calculate the portion of matrix each processor will do */    
   computations = ((double)ZZ.size * ((double)ZZ.size + 1.0) )* 0.5;    
   compPerProc = (double) computations / Num_threads;
    
   curProc = 0;
   startRow[curProc] = 0;
   actualComp[curProc] = 0;
   for(matrixRow=0; matrixRow < ZZ.size; matrixRow++) {
     actualComp[curProc] += ZZ.size - matrixRow - 1;
      if(actualComp[curProc] >= compPerProc ) {
	 endRow[curProc] = matrixRow++;
	 curProc++; 
	 if (curProc>=Num_threads) break;	
	 usedProcs++;
	 startRow[curProc] = matrixRow;
	 actualComp[curProc] = 0;
     }      
   }   
   endRow[usedProcs] = ZZ.size -1;     
  
   for(i=0; i<=usedProcs;i++) {      
     if (pthread_create(&threads[i], &attr, &worker,(void*)i ) != 0) {
       printf("CRITICAL ERROR: cannot create threads!\n");
     }    
   } 
   for(i=0; i<usedProcs+1;i++) {
     pthread_join(threads[i],NULL);
   }
  
   sem_destroy(&cmp_mutex); 
  
   Zserver_fill();
  
   cnt_overlap = 0;
   cnt_nodes = 0;
   for(i=0;i<Num_threads;i++) {
       cnt_overlap += local_cnt_olap[i];
       cnt_nodes += local_cnt_nodes[i];
   }
   return 1;
}
